﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace ZOIL.DataModel
{
    public class TaskQueue<T> : IDisposable where T : class
    {
        private readonly Action<T> _task;
        private readonly object locker = new object();
        private readonly Queue<T> taskQ = new Queue<T>();
        private readonly Thread[] workers;

        public TaskQueue(int workerCount, Action<T> task)
        {
            _task = task;
            workers = new Thread[workerCount];
            // Create and start a separate thread for each worker
            for (int i = 0; i < workerCount; i++)
            {
                (workers[i] = new Thread(Consume) { IsBackground = true}).Start();
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            // Enqueue one null task per worker to make each exit.
            foreach (Thread worker in workers)
            {
                EnqueueItem(null);
            }
            foreach (Thread worker in workers)
            {
                worker.Join();
            }
        }

        #endregion

        public void EnqueueItemIfNotInQueue(T item)
        {
            lock (locker)
            {
                if (!taskQ.Contains(item))
                {
                    taskQ.Enqueue(item);
                    Monitor.PulseAll(locker);
                }
            }
        }

        public void EnqueueItem(T item)
        {
            lock (locker)
            {
                taskQ.Enqueue(item);
                Monitor.PulseAll(locker);
            }
        }

        private void Consume()
        {
            while (true)
            {
                T item;
                lock (locker)
                {
                    while (taskQ.Count == 0)
                    {
                        Monitor.Wait(locker);
                    }
                    item = taskQ.Dequeue();
                }
                if (item == null)
                {
                    return; // This signals our exit
                }
                _task(item);
            }
        }
    }
}